package com.fund.user.service.impl;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fund.enums.AccountType;
import com.fund.user.config.GlobalExceptionHandler;
import com.fund.user.config.SmsTemplate;
import com.fund.user.dao.TUserDao;
import com.fund.user.entity.TUser;
import com.fund.user.service.TUserService;
import com.fund.user.vo.req.UserRegistVo;
import com.fund.user.vo.resp.UserRespVo;
import com.fund.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * (TUser)表服务实现类
 *
 * @author makejava
 * @since 2023-02-15 15:06:58
 */
@Slf4j
@Service("tUserService")
public class TUserServiceImpl extends ServiceImpl<TUserDao, TUser> implements TUserService {


    private static final String SMS_SIGN_ID = "2e65b1bb3d054466b82f0c9d125465e2";
    private static final String TEMPLATE_ID = "908e94ccf08b4476ba6c876d13f084ad";
    private static final long SMS_EXPIRY_SECONDS = 120;
    private static final int EMAIL_CODE_EXPIRY_SECONDS = 120;


    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Resource
    SmsTemplate smsTemplate;


    @Override
    public Page<TUser> getPage(Page<TUser> page, TUser tUser) {
        Page<TUser> pageVo = new Page<>(page.getCurrent(), page.getSize() == 0L ? PageUtil.SIZE : page.getSize(), this.count());
        return this.page(pageVo, new QueryWrapper<TUser>(tUser));
    }

    /**
     * 根据名称模糊分页查询，待填入名称
     */
    @Override
    public Page<TUser> getPageLikeName(Page<TUser> page, TUser tUser) {
        Page<TUser> pageVo = new Page<>(page.getCurrent(), page.getSize() == 0L ? PageUtil.SIZE : page.getSize(), this.count());
        return this.page(pageVo, new QueryWrapper<TUser>().like(StringUtils.isNotBlank(""), "", tUser));
    }

    @Override
    public Boolean insertOne(TUser tUser) {
        boolean save = this.save(tUser);
        if (!save) {
            throw new GlobalExceptionHandler("保存失败");
        }
        return true;
    }

    @Override
    public Boolean updateOneById(TUser tUser) {
        boolean b = this.updateById(tUser);
        if (!b) {
            throw new GlobalExceptionHandler("更新失败");
        }
        return true;
    }

    @Override
    public Boolean deleteListByIds(List<String> idList) {
        List<Long> collect = idList.stream().map(f -> Long.parseLong(f)).collect(Collectors.toList());
        boolean b = this.removeByIds(collect);
        if (!b) {
            throw new GlobalExceptionHandler("删除失败");
        }
        return true;
    }

    @Override
    public TUser getOneById(String id) {
        return this.getById(Long.parseLong(id));
    }

    @Override
    public List<TUser> queryAll(TUser tUser) {
        return this.list(new QueryWrapper<TUser>(tUser));
    }

    public TUser queryOne(TUser tUser) {
        return this.getOne(new QueryWrapper<TUser>(tUser).last("limit 1"));
    }

    /**
     * @param account
     * @param password
     * @return {@link AppResponse}<{@link UserRespVo}>
     */
    @Override
    public AppResponse<UserRespVo> getUserByLogin(String account, String password) {
        TUser tUser = getUserByAccountPassWord(account, password);

        if (ObjectUtil.isEmpty(tUser)) {
            log.debug("登录失败-{}", account);
            throw new GlobalExceptionHandler("登录失败");
        }

        UserRespVo respVo = new UserRespVo();
        // 生成token，UUID可能有-，要全部替换成空
        String accessToken = UUID.randomUUID().toString().replaceAll("-", "");
        respVo.setAccessToken(accessToken);
        respVo.setUserCreateTime(tUser.getUserCreateTime());
        respVo.setUserNickName(tUser.getUserNickName());
        respVo.setUserStatus(tUser.getUserStatus());
        respVo.setUserType(tUser.getUserType());

        // 使用三元运算符来选择设置用户账户或电子邮件
        respVo.setUserAccount(tUser.getUserAccount() != null ? tUser.getUserAccount() : tUser.getUserEmail());

        respVo.setUserImgUrl(tUser.getUserImgUrl());
        respVo.setUserSex(tUser.getUserSex());
        respVo.setUserPlate(tUser.getUserPlate());
        // 获取用户id
        respVo.setUserId(tUser.getUserId());

        // 登录后将accessToken和用户id缓存到Redis,设置失效时间
        stringRedisTemplate.opsForValue().set(accessToken, respVo.getUserId().toString(), 30, TimeUnit.MINUTES);

        log.debug("登录成功-{}", account);
        return AppResponse.ok(respVo);
    }


    @Override
    public AppResponse<Object> loginPhone(String phone, String code) {
        if (StrUtil.isEmpty(phone)) {
            log.debug("请输入邮箱-{}", phone);
            throw new GlobalExceptionHandler("请输入邮箱");
        }

        // 从Redis中获取账户的验证码
        String codeRedis = stringRedisTemplate.opsForValue().get(phone + ":code");

        if (StrUtil.isEmpty(codeRedis)) {
            log.debug("验证码失效-{}", phone);
            throw new GlobalExceptionHandler("验证码已失效");
        }

        if (!codeRedis.equals(code)) {
            log.debug("请重新输入验证码-{}", phone);
            throw new GlobalExceptionHandler("请重新输入验证码");
        }

        // 发送验证码的适合已经验证了邮箱是否存在
        TUser userbyPhone = getUserByPhone(phone);
        log.debug("登录成功-{}", phone);

        // 生成token，UUID可能有-，要全部替换成空
        String accessToken = UUID.randomUUID().toString().replaceAll("-", "");

        // 创建响应对象
        UserRespVo respVo = new UserRespVo();
        respVo.setAccessToken(accessToken);
        respVo.setUserId(userbyPhone.getUserId());

        // 登录后将accessToken和用户id缓存到Redis,设置失效时间
        stringRedisTemplate.opsForValue().set(accessToken, respVo.getUserId().toString(), 30, TimeUnit.MINUTES);

        return AppResponse.ok(respVo);
    }

    @Override
    public AppResponse<Object> loginEmail(String email, String code) {
        if (org.springframework.util.StringUtils.isEmpty(email)) {
            log.debug("请输入邮箱-{}", email);
            throw new GlobalExceptionHandler("请输入邮箱");
        }

        String codeRedis = stringRedisTemplate.opsForValue().get(email + ":code");

        if (org.springframework.util.StringUtils.isEmpty(codeRedis)) {
            log.debug("验证码失效-{}", email);
            throw new GlobalExceptionHandler("验证码已失效");
        }

        if (!codeRedis.equals(code)) {
            log.debug("请重新输入验证码-{}", email);
            throw new GlobalExceptionHandler("请重新输入验证码");
        }

        // 发送验证码的适合已经验证了邮箱是否存在
        TUser userbyEmail = getUserByEmail(email);
        log.debug("登录成功-{}", email);

        // 生成token，UUID可能有-，要全部替换成空
        String accessToken = UUID.randomUUID().toString().replaceAll("-", "");

        UserRespVo respVo = new UserRespVo();
        respVo.setAccessToken(accessToken);
        respVo.setUserId(userbyEmail.getUserId());

        // 登录后将accessToken和用户id缓存到Redis,设置失效时间
        stringRedisTemplate.opsForValue().set(accessToken, respVo.getUserId().toString(), 5, TimeUnit.MINUTES);

        return AppResponse.ok("登录成功", respVo);
    }

    @Override
    public AppResponse<Object> register(UserRegistVo vo) {
        String account = vo.getAccount();

        if (org.springframework.util.StringUtils.isEmpty(account)) {
            throw new GlobalExceptionHandler("用户账户不能为空");
        }

        String code = stringRedisTemplate.opsForValue().get(account + ":code");

        if (org.springframework.util.StringUtils.isEmpty(code)) {
            throw new GlobalExceptionHandler("验证码已失效，请重新发送");
        }

        if (!code.equals(vo.getCode())) {
            throw new GlobalExceptionHandler("验证码不一致，请重新发送");
        }

        if (StrUtil.isEmpty(vo.getNickName())) {
            throw new GlobalExceptionHandler("请填写昵称");
        }

        UserRespVo respVo = new UserRespVo();
        respVo.setUserNickName(vo.getNickName());
        TUser tUserNicker = getUserByNicker(respVo.getUserNickName());

        if (tUserNicker != null) {
            throw new GlobalExceptionHandler("昵称已经被占用");
        }

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String userpswd = vo.getUserpswd();
        respVo.setUserPassword(encoder.encode(userpswd));
        respVo.setUserCreateTime(AppDateUtil.getDateTime());
        respVo.setUserStatus("0");
        respVo.setUserType("1");

        AppResponse<String> response = AccountORUtil.checkAccount(account);


        if (response.getData().equals(AccountType.EMAIL.getName())) {
            respVo.setUserEmail(account);
            TUser acc = getUserByEmail(account);
            if (acc != null) {
                throw new GlobalExceptionHandler("邮箱账户已经存在");
            }
        } else if (response.getData().equals(AccountType.PHONE.getName())) {
            respVo.setUserAccount(account);
            TUser acc = getUserByPhone(account);
            if (acc != null) {
                throw new GlobalExceptionHandler("手机账户已经存在");
            }
        } else {
            throw new GlobalExceptionHandler(response.getMsg());
        }

        boolean sav = saveUser(respVo);
        if (sav) {
            stringRedisTemplate.delete(account + ":code");
            String accessToken = UUID.randomUUID().toString().replaceAll("-", "");
            respVo.setAccessToken(accessToken);
            TUser tUserId = getUserByNicker(respVo.getUserNickName());
            respVo.setUserId(tUserId.getUserId());
            stringRedisTemplate.opsForValue().set(accessToken, respVo.getUserId().toString(), 30, TimeUnit.MINUTES);
            return AppResponse.ok("注册成功", respVo);
        } else {
            throw new GlobalExceptionHandler("注册失败");
        }
    }

    @Override
    public AppResponse<Object> sendSms(String account) {
        AppResponse resp;

        Long expire = stringRedisTemplate.getExpire(account + ":code");
        if (expire != -2) {
            resp = AppResponse.fail(null);
            resp.setMsg(String.format("请%s秒后，再次发送验证码", expire.toString()));
            return resp;
        }

        // 发短信验证码
        String code = HtmlEmailUtil.createCode();

        Map<String, String> querys = new HashMap<>();
        querys.put("mobile", account);
        querys.put("param", String.format("**code**:%s,**minute**:2", code));
        querys.put("smsSignId", SMS_SIGN_ID);
        querys.put("templateId", TEMPLATE_ID);

        smsTemplate.sendSms(querys);

        // 设置过期时间
        stringRedisTemplate.opsForValue().set(account + ":code", code, SMS_EXPIRY_SECONDS, TimeUnit.SECONDS);

        log.debug("发送短信成功-验证码：{}", code);

        resp = AppResponse.ok("ok");
        resp.setMsg("发送短信验证证码成功");
        return resp;
    }


    @Override
    public AppResponse<Object> sendCode(String account) {
        try {
            // 需要校验账户为手机号或邮箱。
            AppResponse<String> response = AccountORUtil.checkAccount(account);
            if (Objects.equals(response.getData(), AccountType.EMAIL.name())) {
                return sendEmailReg(account);
            } else if (Objects.equals(response.getData(), AccountType.PHONE.name())) {
                return sendSms(account);
            } else {
                AppResponse<Object> resp = AppResponse.fail(null);
                resp.setMsg(response.getMsg());
                return resp;
            }
        } catch (Exception e) {
            // 更明确的异常类型
            throw new GlobalExceptionHandler("邮箱服务器错误，请稍后再试");
        }
    }


    @Override
    public AppResponse<Object> sendEmailReg(String email) {
        TUser userByEmail = getUserByEmail(email);

        Long expire = stringRedisTemplate.getExpire(email + ":code");
        if (expire != -2) {
            throw new GlobalExceptionHandler(String.format("请%s秒后，再次发送验证码", String.valueOf(expire)));
        }

        if (userByEmail == null) {
            AppResponse<String> response = sendEmailVerificationCode(email);
            String code = response.getData();
            stringRedisTemplate.opsForValue().set(email + ":code", code, EMAIL_CODE_EXPIRY_SECONDS, TimeUnit.SECONDS);
            log.debug("发送邮箱注册验证码成功-验证码：{}", code);

            return AppResponse.ok("发送注册验证码成功！", code);
        } else {
            throw new GlobalExceptionHandler("账户已存在!");
        }
    }

    @Override
    public AppResponse<Object> sendEmailLog(String email) {
        TUser userByEmail = getUserByEmail(email);

        Long expire = stringRedisTemplate.getExpire(email + ":code");
        if (expire != -2) {
            throw new GlobalExceptionHandler(String.format("请%s秒后，再次发送验证码", String.valueOf(expire)));
        }

        if (StrUtil.isEmpty(userByEmail.getUserNickName())) {
            throw new GlobalExceptionHandler("账户不存在");
        } else {
            AppResponse<String> stringAppResponse = sendEmailLoginVerificationCode(userByEmail.getUserNickName(), email);
            String code = stringAppResponse.getData();

            stringRedisTemplate.opsForValue().set(email + ":code", code, EMAIL_CODE_EXPIRY_SECONDS, TimeUnit.SECONDS);
            log.debug("发送邮箱登录验证码成功-验证码：{}", code);
            return AppResponse.ok("发送登录验证码成功！", code);
        }
    }

    // 提取发送邮箱登录验证码逻辑为方法
    private AppResponse<String> sendEmailLoginVerificationCode(String nickName, String email) {
        return HtmlEmailUtil.sendLog(nickName, email);
    }


    // 提取发送邮箱验证码逻辑为方法
    private AppResponse<String> sendEmailVerificationCode(String email) {
        return HtmlEmailUtil.sendReg(email);
    }


    public Boolean saveUser(UserRespVo vo) {
        TUser tUser = new TUser().setUserAccount(vo.getUserAccount()).setUserEmail(vo.getUserEmail()).setUserNickName(vo.getUserNickName()).setUserPassword(vo.getUserPassword()).setUserCreateTime(vo.getUserCreateTime()).setUserStatus(vo.getUserStatus()).setUserType(vo.getUserType());
        return this.insertOne(tUser);
    }


    private TUser getUserByNicker(String userNickName) {
        TUser user = queryOne(new TUser().setUserNickName(userNickName));
        if (!ObjectUtil.isEmpty(user)) {
            return user;
        }
        return null;
    }


    public TUser getUserByPhone(String account) {
        TUser user = queryOne(new TUser().setUserAccount(account));
        if (!ObjectUtil.isEmpty(user)) {
            return user;
        }
        return null;
    }

    public TUser getUserByEmail(String email) {
        TUser user = queryOne(new TUser().setUserEmail(email));
        if (!ObjectUtil.isEmpty(user)) {
            return user;
        }
        return null;
    }


    public TUser getUserByAccountPassWord(String account, String password) {
        TUser user = null;
        // 需要校验账户为手机号OR邮箱。
        AppResponse<String> response = AccountORUtil.checkAccount(account);
        // 邮箱注册
        String emailStr = "email";
        String phoneStr = "phone";
        List<TUser> list = null;
        if (Objects.equals(response.getData(), emailStr)) {
            // 邮箱注册
            user = queryOne(new TUser().setUserEmail(account));
        } else if (Objects.equals(response.getData(), phoneStr)) {
            // 手机号注册
            user = queryOne(new TUser().setUserAccount(account));
        }

        if (ObjectUtil.isNotEmpty(user)) {
            // BCrypt加密
            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            //密码比较，将用户输入的密码与数据库中拿出来的对比
            // matches编码器，将原始密码和数据库中加密过的给他就好。
            if (encoder.matches(password, user.getUserPassword())) {
                return user;
            }
        }
        return null;
    }
}

